//
//  ActivationControl.m
//  Activation Control
//
//  Created by Keith Duncan on 01/09/2008.
//  Copyright 2008 thirty-three software. All rights reserved.
//

#import "ActivationControl.h"

#import "ActivationCell.h"

@implementation ActivationControl

@dynamic stringValue;
@synthesize sections=_sections;

+ (Class)cellClass {
	return [ActivationCell class];
}

- (id)initWithFrame:(NSRect)frame {
	[super initWithFrame:frame];
	
	_values = [[NSPointerArray pointerArrayWithStrongObjects] retain];
	
	return self;
}

- (void)dealloc {
	[_values release];
	
	[super dealloc];
}

- (BOOL)isFlipped {
	return YES;
}

- (void)setSections:(NSUInteger)value {
	_sections = value;
	[_values setCount:_sections];
	
	[self setNeedsDisplay:YES];
}

- (NSString *)stringValue {
	return [[_values allObjects] componentsJoinedByString:@""];
}

- (void)setStringValue:(NSString *)value {
	if ([value length] > _sections) [NSException raise:NSRangeException format:@"%s passed a string too large to display", _cmd, nil];
	
	for (NSUInteger index = 0; index < [value length]; index++)
		[_values replacePointerAtIndex:index withPointer:[value substringWithRange:NSMakeRange(index, 1)]];
	
	[self setNeedsDisplay:YES];
}

NS_INLINE void CalculateRatio(CGFloat height, CGFloat *sectionWidth, CGFloat *sectionGap) {
	*sectionWidth = (height * 0.85);
	*sectionGap = (height * 0.17);
}

NS_INLINE CGFloat WidthForValues(NSUInteger count, CGFloat width, CGFloat gap) {
	return (count * width) + ((count - 1) * gap);
}

NS_INLINE void ValuesForWidth(NSRect bounds, NSUInteger count, CGFloat *sectionHeight, CGFloat *sectionWidth, CGFloat *sectionGap) {
	CalculateRatio(1.0, sectionWidth, sectionGap);
	CGFloat unitWidth = WidthForValues(count, *sectionWidth, *sectionGap);
	
	CGFloat scaleFactor = MIN(NSWidth(bounds)/unitWidth, NSHeight(bounds)/1.0);
	
	*sectionHeight = scaleFactor;
	*sectionWidth *= scaleFactor;
	*sectionGap *= scaleFactor;
}

NS_INLINE NSRectArray CreateSectionRects(NSRect bounds, NSUInteger count) {
	CGFloat sectionHeight, sectionWidth, sectionGap;
	ValuesForWidth(bounds, count, &sectionHeight, &sectionWidth, &sectionGap);
	
	NSRectArray sectionRects = calloc(count, sizeof(NSRect));
	
	NSPoint origin = bounds.origin;
	for (NSUInteger index = 0; index < count; index++) {
		sectionRects[index] = (NSRect){origin, sectionWidth, sectionHeight};
		origin.x += (sectionWidth + sectionGap);
	}
	
	return sectionRects;
}

- (void)drawRect:(NSRect)rect {
	NSRectArray sectionRects = CreateSectionRects([self bounds], _sections);
	
	NSString *stringValue = [self stringValue];
	
	for (NSUInteger index = 0; index < _sections; index++) {
		[[self cell] setHighlighted:([self isEnabled] && index == _selectedSection)];
		
		if (NSLocationInRange(index, NSMakeRange(0, [stringValue length])))
			[[self cell] setObjectValue:[stringValue substringWithRange:NSMakeRange(index, 1)]];
		else 
			[[self cell] setObjectValue:nil];
		
		[[self cell] drawWithFrame:sectionRects[index] inView:self];
	}
	
	free(sectionRects);
}

- (BOOL)acceptsFirstResponder {
	return [self isEnabled];
}

- (void)_setSelection:(NSUInteger)value {
	if (!NSLocationInRange(value, NSMakeRange(0, _sections)) && value != NSIntegerMax) return;
	
	_selectedSection = value;
	
	[self setNeedsDisplay:YES];
}

- (void)mouseDown:(NSEvent *)event {
	NSRectArray sectionRects = CreateSectionRects([self bounds], _sections);
	NSPoint clickPoint = [self convertPoint:[event locationInWindow] fromView:nil];
	
	for (NSUInteger index = 0; index < _sections; index++) {
		if (!NSMouseInRect(clickPoint, sectionRects[index], [self isFlipped])) continue;
		
		[self _setSelection:index];
		break;
	}
	
	free(sectionRects);
}

- (void)keyDown:(NSEvent *)event {	
	[self interpretKeyEvents:[NSArray arrayWithObject:event]];
}

- (void)moveLeft:(id)sender {
	[self _setSelection:(_selectedSection-1)];
}

- (void)moveRight:(id)sender {
	[self _setSelection:(_selectedSection+1)];
}

- (void)insertText:(id)string {
	if (_selectedSection == NSIntegerMax) return;
	
	[_values replacePointerAtIndex:_selectedSection withPointer:[string substringWithRange:NSMakeRange(0, 1)]];
	[self setNeedsDisplay:YES];
	
	NSInteger proposedSelection = _selectedSection+1;
	[self _setSelection:(proposedSelection < _sections ? proposedSelection : NSIntegerMax)];
}

- (void)deleteBackward:(id)sender {
	NSInteger proposedSelection = _selectedSection-1;
	if (proposedSelection < 0) return;
	
	[_values removePointerAtIndex:proposedSelection];
	
	[self _setSelection:proposedSelection];
	
	[self setNeedsDisplay:YES];
}

@end
